package com.asen.commons.core.base.service.impl;

import com.asen.commons.annotation.FieldIgnore;
import com.asen.commons.core.base.domain.BaseEntry;
import com.asen.commons.core.base.domain.CoreBase;
import com.asen.commons.core.base.service.BaseEntryService;
import com.asen.commons.core.base.service.CoreBaseService;
import com.asen.commons.core.meta.dao.Condition;
import com.asen.commons.core.meta.entity.EntityObject;
import com.asen.commons.core.meta.entity.FieldObject;
import com.asen.commons.utils.BaseAssert;
import com.asen.commons.utils.SpringUtil;
import com.asen.commons.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 基类
 *
 * @author Gary
 * @version 1.0
 * @date 2021-04-29
 */
public class AbstractCoreBaseServiceImpl<T extends CoreBase> extends AbstractCoreServiceImpl<T> implements CoreBaseService<T> {

    @Override
    protected void beforeAdd(T model) throws Exception {
        super.beforeAdd(model);
        model.setIsDeleted(Boolean.FALSE);
    }

    @Override
    protected void doAdd(T model) throws Exception {
        super.doAdd(model);

        // 新增子对象数据
        //for (FieldObject subFo : getSubFieldList()) {
        //    SubBase subBase = (SubBase) subFo.getValue(model);
        //    addSubData(subFo.getJavaType(), subBase, model);
        //}

        // 新增分录
        for (Map.Entry<Class<? extends BaseEntry>, FieldObject> entry : getEntryMap().entrySet()) {
            FieldObject fo = entry.getValue();
            List<? extends BaseEntry> entryList = (List<? extends BaseEntry>) fo.getValue(model);
            addEntry(entry.getKey(), entryList, model);
        }
    }

    @Override
    protected void beforeUpdate(T model) throws Exception {
        super.beforeUpdate(model);
        model.setIsDeleted(Boolean.FALSE);
    }

    @Override
    protected void doUpdate(T model) throws Exception {
        super.doUpdate(model);

        // 更新子对象数据
        //for (FieldObject subFo : getSubFieldList()) {
        //    SubBase subBase = (SubBase) subFo.getValue(model);
        //    updateSubData(subFo.getJavaType(), subBase, model);
        //}

        // 更新分录
        for (Map.Entry<Class<? extends BaseEntry>, FieldObject> entry : getEntryMap().entrySet()) {
            FieldObject fo = entry.getValue();
            List<? extends BaseEntry> entryList = (List<? extends BaseEntry>) fo.getValue(model);
            updateEntry(entry.getKey(), entryList, model);
        }
    }

    @Override
    protected void beforeRemove(T model) throws Exception {
        super.beforeRemove(model);
        model.setIsDeleted(Boolean.TRUE);
    }

    @Override
    protected void doRemove(T model) throws Exception {
        super.doRemove(model);

        // 删除子对象数据
        //for (FieldObject subFo : getSubFieldList()) {
        //    SubBase subBase = (SubBase) subFo.getValue(model);
        //    removeSubData(subFo.getJavaType(), subBase, model);
        //}

        // 删除分录
        for (Map.Entry<Class<? extends BaseEntry>, FieldObject> entry : getEntryMap().entrySet()) {
            removeEntry(entry.getKey(), model);
        }
    }

    @Override
    protected T doGet(Long id) throws Exception {
        T model = super.doGet(id);

        if (model != null) {
            // 查询子对象数据
            //for (FieldObject fo : getSubFieldList()) {
            //    SubBase subBase = (SubBase) fo.getValue(model);
            //    fo.setValue(model, getSubData(fo.getJavaType(), model));
            //}

            // 查询分录
            for (Map.Entry<Class<? extends BaseEntry>, FieldObject> entry : getEntryMap().entrySet()) {
                FieldObject fo = entry.getValue();
                fo.setValue(model, getEntry(entry.getKey(), model));
            }
        }

        return model;
    }

    //protected void addSubData(Class<?> subClazz, SubBase subBase, T model) throws Exception {
    //    if (subClazz == null || subBase == null || model == null || IdUtils.isEmpty(model.getId())) {
    //        return;
    //    }
    //
    //    SubBaseService subBaseService = (SubBaseService) BillTypeUtils.getServiceBeanQuietly(subClazz.getName());
    //    BaseAssert.notNull(subBaseService, "no match service. billType is: " + subClazz.getName());
    //
    //    subBase.setParentId(model.getId());
    //    subBaseService.add(subBase);
    //}

    //protected void updateSubData(Class<?> subClazz, SubBase subBase, T model) throws Exception {
    //    if (subClazz == null || subBase == null || model == null || IdUtils.isEmpty(model.getId())) {
    //        return;
    //    }
    //
    //    SubBaseService subBaseService = (SubBaseService) BillTypeUtils.getServiceBeanQuietly(subClazz.getName());
    //    BaseAssert.notNull(subBaseService, "no match service. billType is: " + subClazz.getName());
    //
    //    subBase.setParentId(model.getId());
    //    if (IdUtils.isNotEmpty(subBase.getId())) {
    //        subBaseService.update(subBase);
    //    } else {
    //        subBaseService.add(subBase);
    //    }
    //}

    //protected void removeSubData(Class<?> subClazz, SubBase subBase, T model) throws Exception {
    //    if (subClazz == null || subBase == null || IdUtils.isEmpty(model.getId())) {
    //        return;
    //    }
    //
    //    SubBaseService subBaseService = (SubBaseService) BillTypeUtils.getServiceBeanQuietly(subClazz.getName());
    //    BaseAssert.notNull(subBaseService, "no match service. billType is: " + subClazz.getName());
    //
    //    subBaseService.remove(subBase);
    //}

    //protected SubBase<T> getSubData(Class<?> subClazz, T model) throws Exception {
    //    if (subClazz == null || model == null || IdUtils.isEmpty(model.getId())) {
    //        return null;
    //    }
    //
    //    SubBaseService subBaseService = (SubBaseService) BillTypeUtils.getServiceBeanQuietly(subClazz.getName());
    //    BaseAssert.notNull(subBaseService, "no match service. billType is: " + subClazz.getName());
    //
    //    Condition condition = new Condition();
    //    condition.equal("parentId", model.getId());
    //    SubBase result = (SubBase) subBaseService.getBy(condition);
    //
    //    if (result != null && (!subBaseService.getEntryMap().isEmpty()
    //            || !subBaseService.getSubFieldList().isEmpty())) {
    //        SubBase originalResult = result;
    //        result = (SubBase) subBaseService.get(result.getId());
    //    }
    //
    //    return result;
    //}

    private Object lock = new Object();
    //private List<FieldObject> subFieldList;

    //@Override
    //public List<FieldObject> getSubFieldList() throws Exception {
    //    if (subFieldList == null) {
    //        synchronized (lock) {
    //            if (subFieldList == null) {
    //                subFieldList = new ArrayList<>(6);
    //                EntityObject eo = getEntity();
    //                for (FieldObject fo : eo.getFieldList()) {
    //                    if (fo.isIgnore()) {
    //                        continue;
    //                    }
    //
    //                    if (fo.isAnnotationPresent(FieldSubordinate.class) && SubBase.class.isAssignableFrom(fo.getJavaType())) {
    //                        subFieldList.add(fo);
    //                    }
    //                }
    //            }
    //        }
    //    }
    //    return subFieldList;
    //}

    protected void addEntry(Class<? extends BaseEntry> entryClazz, List<? extends BaseEntry> entryList, T parent) throws Exception {
        if (entryClazz == null || entryList == null || entryList.isEmpty() || parent == null) {
            return;
        }

        BaseEntryService entryService = getEntryServiceBean(entryClazz);

        // 新增分录
        entryService.addEntry(entryList, parent.getId());
    }

    protected void updateEntry(Class<? extends BaseEntry> entryClazz, List<? extends BaseEntry> entryList, T parent) throws Exception {
        if (entryClazz == null || parent == null) {
            return;
        }

        BaseEntryService entryService = getEntryServiceBean(entryClazz);

        if (CollectionUtils.isEmpty(entryList)) {
            // 删除分录
            entryService.removeEntry(parent.getId());
        } else {
            // 更新分录
            entryService.updateEntry(entryList, parent.getId());
        }
    }

    protected void removeEntry(Class<? extends BaseEntry> entryClazz, T parent) throws Exception {
        if (entryClazz == null || parent == null) {
            return;
        }

        BaseEntryService entryService = getEntryServiceBean(entryClazz);

        // 删除分录
        entryService.removeEntry(parent.getId());
    }

    public List<? extends BaseEntry> getEntry(Class<? extends BaseEntry> entryClazz, T parent) throws Exception {
        if (entryClazz == null || parent == null) {
            return null;
        }

        BaseEntryService entryService = getEntryServiceBean(entryClazz);

        Condition condition = new Condition();
        condition.equal("parentId", parent.getId());
        condition.orderByAsc("seqNum");
        List<BaseEntry> result = entryService.getList(condition);
        //if (!result.isEmpty() && (!entryService.getEntryMap().isEmpty() || !entryService.getSubFieldList().isEmpty())) {
        if (!result.isEmpty() && !entryService.getEntryMap().isEmpty()) {
            List<BaseEntry> originalResult = result;
            result = new ArrayList<>();
            for (BaseEntry entry : originalResult) {
                result.add((BaseEntry) entryService.get(entry.getId()));
            }
        }

        return result;
    }

    private Map<Class<? extends BaseEntry>, FieldObject> entryMap;

    @Override
    public Map<Class<? extends BaseEntry>, FieldObject> getEntryMap() throws Exception {
        if (entryMap == null) {
            synchronized (lock) {
                if (entryMap == null) {
                    entryMap = new HashMap<Class<? extends BaseEntry>, FieldObject>(6);
                    EntityObject eo = getEntity();
                    for (FieldObject fo : eo.getFieldList()) {
                        if (fo.isAnnotationPresent(FieldIgnore.class)) {
                            continue;
                        }

                        addEntryMap(fo, entryMap);
                    }
                }
            }
        }
        return entryMap;
    }

    private void addEntryMap(FieldObject fo, Map<Class<? extends BaseEntry>, FieldObject> entryMap) throws Exception {
        if (List.class.isAssignableFrom(fo.getJavaType())) {
            Type[] argTypes = fo.getActualParameterTypeArgs();
            if (argTypes != null && argTypes.length == 1) {
                Class<?> entryClazz = (Class<?>) argTypes[0];
                ParameterizedType superClassType = (ParameterizedType) entryClazz.getGenericSuperclass();
                if (!ArrayUtils.isEmpty(superClassType.getActualTypeArguments())) {
                    Class<?> genericType = (Class<?>) superClassType.getActualTypeArguments()[0];
                    if (getModelClass().isAssignableFrom(genericType)) {
                        entryMap.put((Class<? extends BaseEntry>) entryClazz, fo);
                    }
                }
            }
        }
    }

    /**
     * 获取分录Service实现类
     *
     * @param entryName
     * @return
     * @throws Exception
     */
    @Override
    public BaseEntryService getEntryService(String entryName) throws Exception {
        if (StringUtils.isBlank(entryName)) {
            return null;
        }
        Set<Map.Entry<Class<? extends BaseEntry>, FieldObject>> entrySet = getEntryMap().entrySet();
        for (Map.Entry<Class<? extends BaseEntry>, FieldObject> entry : entrySet) {
            FieldObject fo = entry.getValue();
            if (!entryName.equals(fo.getName())) {
                continue;
            }
            return getEntryServiceBean(entry.getKey());
        }
        return null;
    }

    private BaseEntryService getEntryServiceBean(Class<? extends BaseEntry> clazz) throws Exception{
        Object entryServiceBean = SpringUtil.getBean(clazz.getName() + "Service");
        BaseAssert.notNull(entryServiceBean, "no match service. class name is: " + clazz.getName());
        return (BaseEntryService)entryServiceBean;
    }

}
